#ifndef TOPO_H
#define TOPO_H

#include "attrs.h"
#include "flow.h"
#include "spdlog/spdlog.h"

#include <algorithm>
#include <cstdio>
#include <fstream>
#include <httplib.h>
#include <iostream>
#include <limits>
#include <nlohmann/json.hpp>
#include <signal.h>
#include <string>
#include <sw/redis++/redis++.h>
#include <thread>
#include <unistd.h>
#include <unordered_map>
#include <vector>

using namespace std;
using nlohmann::json;

extern std::unordered_map<std::string, std::pair<std::string, int>> disDictionary;

class Topo
{
public:
    Topo();
    void run(void);

    /* 业务相关 */
    void read_flow(int);                     /* 读取业务信息 */
    void add_flow(string, string, int, int); /* 添加业务 */
    void del_flow(string, string, int);      /* 删除业务 */
    void set_local_rule();                   /* 设置转发到本地端口的流表 */

    /* 业务路由 */
    void update_routing(void); /* 更新业务的路由流表，生成流表到 flow_table */
    void forward_flow_rules(const string &, const string &,
                            const string &, const string &,
                            const vector<string> &,
                            vector<string> &, Flow &, int, int); /* 正向流表 */
    void backward_flow_rules(const string &, const string &,
                             const string &, const string &,
                             const vector<string> &,
                             vector<string> &, Flow &, int, int); /* 反向流表 */

    uint32_t m_slot;           /* 记录仿真时隙 */
    string m_slice;            /* 当前时间片开始的时隙 */
    set<string> nodes;         /* 卫星节点ID集合 */
    map<string, Flow> flows;   /* 业务流<ID，Flow> */
    vector<string> flow_rules; /* 每个业务需要配置的流表 */
    httplib::Client http_cli;  /* 用于与路由模块通信，请求路由 */
    pthread_rwlock_t flows_rwlock;

private:
    sw::redis::Redis redis_cli; /* 读取topo信息 */

    /* 维护网络拓扑 */
    void read_topo(void);     /* 读取拓扑信息 */
    void start_ovs(void);     /* 创建 容器并启动容器，并执行 ovs 初始化脚本 */
    void set_veth_peer(void); /* 创建 veth peer，并将 veth peer 分配给容器 */

    string get_ip(const string &node);
    string get_mac(const string &node);
    bool check_flow(string &, string &, int);
};

inline Topo::Topo() : http_cli(ROUTE_HOST), redis_cli(REDIS_TOPO)
{
    m_slot = 0;
    m_slice = "0";
    redis_cli.auth(REDIS_PASSWD);
    http_cli.set_keep_alive(true);

    pthread_rwlock_init(&flows_rwlock, NULL);
}

inline string Topo::get_ip(const string &node)
{
    return format(IP_ADDR, node.substr(0, 2), stoi((node.substr(2, 2))));
}

inline string Topo::get_mac(const string &node)
{
    return format(MAC_ADDR, node.substr(0, 2), (node.substr(2, 2)));
}

inline bool Topo::check_flow(string &src, string &dst, int tos)
{
    if (!nodes.count(src) || !nodes.count(dst))
    {
        spdlog::error("不存在节点 {}, {}", src, dst);
        return false;
    }

    if (src == dst || tos > 7)
    {
        spdlog::error("输入不合法，tos: {}，有效范围：0~7", tos);
        return false;
    }

    if (src > dst)
    {
        swap(src, dst);
    }
    return true;
}

void Topo::run(void)
{
    read_topo();
    start_ovs();
    set_veth_peer();
    set_local_rule();
}

void Topo::read_topo(void)
{
    unordered_map<string, string> ump;

    redis_cli.hgetall(TOPOLOGY_KEY, std::inserter(ump, ump.begin()));
    for (auto &p : ump)
        nodes.insert(p.first);

    spdlog::info("拓扑信息 >>> 节点数：{}", nodes.size());
}

void Topo::start_ovs(void)
{
    string arp_cmd, docker_cmd, user_send, user_recv, band_cmd;
    spdlog::info("根据节点列表，创建Docker并启动OVS");
    for (const string &node : nodes)
    {
#ifdef MPLS_RULE
        docker_cmd = format(CMD_CREATE_DOCKER, node, OVS_DPDK_MPLS);
#else
        docker_cmd = format(CMD_CREATE_DOCKER, node, OVS_DPDK);
#endif
        system(docker_cmd.c_str());
        spdlog::debug("{} 启动完成", node);
    }
    sleep(2); // 等待OVS启动完成

    spdlog::info("在所有 docker 中 运行 arp.sh/user_send/user_recv/band");
    for (const string &node : nodes)
    {
        arp_cmd = format(CMD_SET_ARP, node);
        system(arp_cmd.c_str());
#ifdef MPLS_RULE
        user_send = format(CMD_USER_SEND, node);
        user_recv = format(CMD_USER_RECV, node);
        band_cmd = format(CMD_BAND, node);
        system(user_send.c_str());
        system(user_recv.c_str());
        system(band_cmd.c_str());
#endif
        spdlog::debug("{} 运行成功", node);
    }
}

void Topo::set_veth_peer(void)
{
    uint64_t begin = get_time_ns(), end;
    spdlog::info("挂载 veth peer...");
    system(CMD_SET_VETH_PAIR.c_str());
    end = get_time_ns();
    spdlog::info("挂载完成，用时 {} s", (end - begin) / S_TO_NS);
}

void Topo::forward_flow_rules(const string &srcmac, const string &dstmac,
                              const string &srcip, const string &dstip,
                              const vector<string> &path,
                              vector<string> &per_flow_rules, Flow &f,
                              int old_delay, int new_delay)
{
    int len = path.size();
    int path_fork = -1;
    int delay = 0;

#ifdef MPLS_RULE
    pthread_mutex_lock(&f.flow_mutex);
    string begin = f.every_hop ? rules::PUSH_PUSH_PUSH : rules::PUSH_PUSH;
    string middle = f.every_hop ? rules::FORWARD_3MPLS : rules::FORWARD_2MPLS;
    string end = f.every_hop ? rules::POP_POP_POP : rules::POP_POP;

    string begin_timeout = f.every_hop ? rules::PUSH_PUSH_PUSH_TIMEOUT : rules::PUSH_PUSH_TIMEOUT;
    string middle_timeout = f.every_hop ? rules::FORWARD_3MPLS_TIMEOUT : rules::FORWARD_2MPLS_TIMEOUT;
    pthread_mutex_unlock(&f.flow_mutex);
#endif
    if (old_delay > new_delay && path != f.path)
    {
        spdlog::debug("flow: {}, old_path: {}, old_delay: {}, new_path: {}, new_delay: {}",
                      f.id, f.path, old_delay, path, new_delay);
        //分离路由情况
        if (f.dis == 1 && f.dis == 2)
        {
            len -= 2;
        }
        for (int i = 1; i < len; i++)
        {
            if (path[i] != f.path[i])
            {
                path_fork = i - 1;
                break;
            }
        }
        delay = old_delay - new_delay + 5;
        spdlog::debug("在节点：{} 处进行缓存，缓存 {}ms", path[path_fork], delay);
    }

    /* 正向流表 */
    for (int i = 0; i < len; i++)
    {
        if (i == 0)
        {

            if (f.dis == 0)
            {
                /* 起始节点 */
                if (i == path_fork)
                {
                    per_flow_rules.emplace_back(format(rules::IP_RULE_BODY_TIMEOUT,
                                                       path[i], path[i + 1], srcip, dstip, delay));
                }

                per_flow_rules.emplace_back(format(rules::IP_RULE_BODY,
                                                   path[i], path[i + 1], srcip, dstip));
            }
            else
            {
                /* 起始节点 */
                if (i == path_fork)
                {
                    per_flow_rules.emplace_back(format(rules::IP_RULE_BODY_DIS_TIMEOUT,
                                                       path[i], path[i + 1], srcip, dstip, delay));
                }

                per_flow_rules.emplace_back(format(rules::IP_RULE_BODY_DIS,
                                                   path[i], path[i + 1], srcip, dstip));
            }
#ifdef MPLS_RULE
            if (i == path_fork)
            {
                per_flow_rules.emplace_back(format(begin_timeout, path[i], path[i + 1],
                                                   srcmac, dstmac, f.forward_label, UDP,
                                                   f.dscp, f.ecn, delay));
                per_flow_rules.emplace_back(format(begin_timeout, path[i], path[i + 1],
                                                   srcmac, dstmac, f.forward_label, TCP,
                                                   f.dscp, f.ecn, delay));
            }
            per_flow_rules.emplace_back(format(begin, path[i], path[i + 1],
                                               srcmac, dstmac, f.forward_label, UDP,
                                               f.dscp, f.ecn));
            per_flow_rules.emplace_back(format(begin, path[i], path[i + 1],
                                               srcmac, dstmac, f.forward_label, TCP,
                                               f.dscp, f.ecn));
#endif
        }
        else if (i == len - 1)
        {
            /* 目的节点 */
#ifdef MPLS_RULE
            per_flow_rules.emplace_back(format(end, path[i], f.forward_label));
#endif
        }
        else
        {
            /* 正向中间节点 */
            if (i == path_fork)
            {
                per_flow_rules.emplace_back(format(rules::IP_RULE_BODY, path[i],
                                                   path[i + 1], srcip, dstip));
            }
            per_flow_rules.emplace_back(format(rules::IP_RULE_BODY, path[i],
                                               path[i + 1], srcip, dstip));
#ifdef MPLS_RULE
            if (i == path_fork)
            {
                per_flow_rules.emplace_back(format(middle_timeout, path[i],
                                                   path[i + 1], f.forward_label, delay));
            }
            per_flow_rules.emplace_back(format(middle, path[i],
                                               path[i + 1], f.forward_label));
#endif
        }
    }
}

void Topo::backward_flow_rules(const string &srcmac, const string &dstmac,
                               const string &srcip, const string &dstip,
                               const vector<string> &path,
                               vector<string> &per_flow_rules, Flow &f,
                               int old_delay, int new_delay)
{
    int len = path.size();
    int path_fork = -1;
    int delay = 0;

#ifdef MPLS_RULE
    pthread_mutex_lock(&f.flow_mutex);
    string begin = f.every_hop ? rules::PUSH_PUSH_PUSH : rules::PUSH_PUSH;
    string middle = f.every_hop ? rules::FORWARD_3MPLS : rules::FORWARD_2MPLS;
    string end = f.every_hop ? rules::POP_POP_POP : rules::POP_POP;

    string begin_timeout = f.every_hop ? rules::PUSH_PUSH_PUSH_TIMEOUT : rules::PUSH_PUSH_TIMEOUT;
    string middle_timeout = f.every_hop ? rules::FORWARD_3MPLS_TIMEOUT : rules::FORWARD_2MPLS_TIMEOUT;
    pthread_mutex_unlock(&f.flow_mutex);
#endif
    if (old_delay > new_delay && path != f.path)
    {
        spdlog::debug("flow: {}, old_path: {}, old_delay: {}, new_path: {}, new_delay: {}",
                      f.id, f.path, old_delay, path, new_delay);
        for (int i = len - 2; i >= 0; i--)
        {
            if (path[i] != f.path[i])
            {
                path_fork = i + 1;
                break;
            }
        }
        delay = old_delay - new_delay + 5;
        spdlog::debug("在节点：{} 处进行缓存，缓存 {}ms", path[path_fork], delay);
    }

    /* 正向流表 */
    for (int i = len - 1; i >= 0; i--)
    {
        if (i == len - 1)
        {
            if (f.dis == 0)
            {
                /* 起始节点 */
                if (i == path_fork)
                {
                    per_flow_rules.emplace_back(format(rules::IP_RULE_BODY_TIMEOUT,
                                                       path[i], path[i - 1], srcip, dstip));
                }
                per_flow_rules.emplace_back(format(rules::IP_RULE_BODY,
                                                   path[i], path[i - 1], srcip, dstip));
            }
            else
            {
                /* 起始节点 */
                if (i == path_fork)
                {
                    per_flow_rules.emplace_back(format(rules::IP_RULE_BODY_DIS_TIMEOUT,
                                                       path[i], path[i - 1], srcip, dstip));
                }
                per_flow_rules.emplace_back(format(rules::IP_RULE_BODY_DIS,
                                                   path[i], path[i - 1], srcip, dstip));
            }
#ifdef MPLS_RULE
            if (i == path_fork)
            {
                per_flow_rules.emplace_back(format(begin_timeout, path[i], path[i - 1],
                                                   srcmac, dstmac, f.backward_label, UDP,
                                                   f.dscp, f.ecn, delay));
                per_flow_rules.emplace_back(format(begin_timeout, path[i], path[i - 1],
                                                   srcmac, dstmac, f.backward_label, TCP,
                                                   f.dscp, f.ecn, delay));
            }
            per_flow_rules.emplace_back(format(begin, path[i], path[i - 1],
                                               srcmac, dstmac, f.backward_label, UDP,
                                               f.dscp, f.ecn));
            per_flow_rules.emplace_back(format(begin, path[i], path[i - 1],
                                               srcmac, dstmac, f.backward_label, TCP,
                                               f.dscp, f.ecn));
#endif
        }
        else if (i == 0)
        {
            /* 目的节点 */
#ifdef MPLS_RULE
            per_flow_rules.emplace_back(format(end, path[i], f.backward_label));
#endif
        }
        else
        {
            /* 中间节点 */
            if (i == path_fork)
            {
                per_flow_rules.emplace_back(format(rules::IP_RULE_BODY_TIMEOUT, path[i],
                                                   path[i - 1], srcip, dstip, delay));
            }
            per_flow_rules.emplace_back(format(rules::IP_RULE_BODY, path[i],
                                               path[i - 1], srcip, dstip));
#ifdef MPLS_RULE
            if (i == path_fork)
            {
                per_flow_rules.emplace_back(format(middle_timeout, path[i],
                                                   path[i - 1], f.backward_label, delay));
            }
            per_flow_rules.emplace_back(format(middle, path[i],
                                               path[i - 1], f.backward_label));
#endif
        }
    }
}

void Topo::update_routing(void)
{
    string srcmac, dstmac, srcip, dstip;
    string slot_ = to_string(m_slot);
    string forward, backward;
    vector<string> path;
    vector<string> per_flow_rules;
    int old_delay, new_delay;

    flow_rules.clear();

    // 遍历所有业务，更新流表
    pthread_rwlock_rdlock(&flows_rwlock);
    for (auto &item : flows)
    {
        Flow &f = item.second;
        old_delay = f.theory_delay;
        new_delay = INT_MAX;

        /* 新时间片  |  强制更新
         *  *True*   |    True      case 1
         *  *True*   |    False     case 1,2 都直接以新时间片的方式处理
         *   False   |   *True*     case 3   强制更新处理
         *   False   |    False     case 4   continue
         */
        pthread_mutex_lock(&f.flow_mutex);
        if (f.route.contains(slot_))
        {
            /* case 1,2 */
            f.slice = slot_;
            if (m_slice != slot_)
                spdlog::debug("切换时间片，起始时刻为：{}", slot_);
            m_slice = slot_;
            path = f.route[m_slice]["path"];
            f.theory_delay = stoi(string(f.route[m_slice]["maxDelay"])) / 100;
            new_delay = f.theory_delay;
        }
        else if (f.update_rule)
        {
            /* case 3 */
            path = f.route[m_slice]["path"];
        }
        else
        {
            /* case 4 不需要处理该流 */
            pthread_mutex_unlock(&f.flow_mutex);
            continue;
        }
        pthread_mutex_unlock(&f.flow_mutex);

        /* 一般情况下，只有路由发生了变化，才需要重新下发流表，但存在
         * 需要立刻下发逐跳测量流表的情况，即使路径一致，也需要重新下发 */
        if (path != f.path || f.update_rule)
        {
            if (f.update_rule)
            {
                f.update_rule = false;
            }
            else
            {
                f.cnt = 0;
            }

            per_flow_rules.clear();
            srcmac = get_mac(path[0]);
            dstmac = get_mac(path.back());
            srcip = get_ip(path[0]);
            dstip = get_ip(path.back());

            /* 正向流表 */
            forward_flow_rules(srcmac, dstmac, srcip, dstip, path, per_flow_rules, f, old_delay, new_delay);
            /* 反向流表 */
            backward_flow_rules(dstmac, srcmac, dstip, srcip, path, per_flow_rules, f, old_delay, new_delay);

            f.path = path;
            flow_rules.emplace_back(format(rules::BATCH_RULES,
                                           fmt::join(per_flow_rules, ",")));
        }
    }
    pthread_rwlock_unlock(&flows_rwlock);
}

void Topo::read_flow(int count)
{
    string path, flow_id;
    uint64_t begin = get_time_ns(), end;
    vector<string> nodes_id(nodes.begin(), nodes.end());
    int32_t node_nums = nodes.size();

    spdlog::debug("向路由模块发送 CLEAR FLOW CACHE，清空缓存");
    auto res = http_cli.Post(CLEAR_CACHE);
    if (!res)
    {
        spdlog::error("向路由模块发送 clear flow cache 失败, err: {}",
                      to_string(res.error()));
    }
    else
    {
        spdlog::info("CLEAR CACHE: {}", res->body);
    }

    // for (int i = 0, j; i < count && i < node_nums; i++) {
    //     for (j = i+1; j < count && j < node_nums; j++) {
    //         flow_id = format(FLOW_ID, nodes_id[i], nodes_id[j], 0);
    //         path = format(ROUTING_TOS_PATH, nodes_id[i], nodes_id[j], 0);

    //         auto res = http_cli.Post(path);
    //         if (!res || res->status != 200) {
    //             spdlog::error("flow {} 请求路由信息失败, err: {}",
    //                           flow_id, to_string(res.error()));
    //             continue;
    //         }

    //         flows.insert({flow_id, Flow(nodes_id[i], nodes_id[j], 0, res->body)});
    //     }
    // }

    end = get_time_ns();
    spdlog::info("添加 {} 条流用时: {}ms", flows.size(),
                 (end - begin) / MS_TO_NS);
}

void Topo::add_flow(string src, string dst, int tos, int isDisjoint)
{
    string path, tmp, flow_id, flow_id2;

    if (!check_flow(src, dst, tos))
        return;
    if (isDisjoint == 1)
    {
        flow_id = format(FLOW_ID, src, dst, tos, 1);
        if (flows.find(flow_id) != flows.end())
        {
            spdlog::info("此业务 {} 已存在！无需添加", flow_id);
            return;
        }
        // 调试
        spdlog::warn(" isDisjoint == 1 flow_id {}  = ", flow_id);

        tmp = DisROUTING_TOS_PATH;
        path = format(tmp, src, dst, tos);
        auto res = http_cli.Post(path);
        if (!res)
        {
            spdlog::error("flow {} 请求分离路由失败！Error: {}",
                          flow_id, to_string(res.error()));
            return;
        }

        if (res->status != 200)
        {
            spdlog::warn("flow {} 请求分离路由异常！status_code: {}, body: {}",
                         flow_id, res->status, res->body);
            return;
        }

        // 解析原始 JSON 数据
        json parsedJson = json::parse(res->body);

        // 构建两个新的 JSON 数据对象
        json newData1, newData2;

        for (auto &item : parsedJson.items())
        {
            // 对每个键值对进行处理
            std::string key = item.key();
            json value = item.value();

            // 取出每个键值对中的第一个对象，并添加到 newData1
            newData1[key] = value.at(0);

            // 取出每个键值对中的第二个对象，并添加到 newData2
            newData2[key] = value.at(1);
        }

        spdlog::warn(" newData1 =  {}  ", newData1);
        spdlog::warn(" newData2 =  {}  ", newData2);

        std::string jsonString = newData1.dump();
        std::string jsonStrin2 = newData2.dump();

        spdlog::warn(" jsonString =  {}  ", jsonString);
        spdlog::warn(" jsonStrin2 =  {}  ", jsonStrin2);

        pthread_rwlock_wrlock(&flows_rwlock);
        flows.insert({flow_id, Flow(src, dst, tos, jsonString, 1)});
        pthread_rwlock_unlock(&flows_rwlock);

        flow_id2 = format(FLOW_ID, src, dst, tos, 0);

        spdlog::warn(" isDisjoint == 1 flow_id2 {}  = ", flow_id2);

        pthread_rwlock_wrlock(&flows_rwlock);
        flows.insert({flow_id2, Flow(src, dst, tos, jsonStrin2, 0)});
        pthread_rwlock_unlock(&flows_rwlock);
    }
    else
    {
        flow_id = format(FLOW_ID, src, dst, tos, 0);
        spdlog::warn(" isDisjoint == 0 flow_id {}  = ", flow_id);

        if (flows.find(flow_id) != flows.end())
        {
            spdlog::info("此业务 {} 已存在！无需添加", flow_id);
            return;
        }

        tmp = (src[0] == '2' || dst[0] == '2') ? STATION_TOS_PATH : ROUTING_TOS_PATH;
        path = format(tmp, src, dst, tos);
        auto res = http_cli.Post(path);
        if (!res)
        {
            spdlog::error("flow {} 请求路由失败！Error: {}",
                          flow_id, to_string(res.error()));
            return;
        }

        if (res->status != 200)
        {
            spdlog::warn("flow {} 请求路由异常！status_code: {}, body: {}",
                         flow_id, res->status, res->body);
            return;
        }
        pthread_rwlock_wrlock(&flows_rwlock);
        spdlog::warn(" flows.insert 1 ");
        flows.insert({flow_id, Flow(src, dst, tos, res->body, 0)});
        spdlog::warn(" flows.insert 2 ");
        pthread_rwlock_unlock(&flows_rwlock);
    }
}

void Topo::del_flow(string src, string dst, int tos)
{
    string path, tmp, flow_id;
    if (!check_flow(src, dst, tos))
        return;

    flow_id = format(FLOW_ID, src, dst, tos);
    if (flows.find(flow_id) == flows.end())
    {
        spdlog::info("业务：{} 不存在，无需删除！", flow_id);
        return;
    }

    pthread_rwlock_wrlock(&flows_rwlock);
    flows.erase(flow_id);
    pthread_rwlock_unlock(&flows_rwlock);
    spdlog::info("业务：{} 删除成功！", flow_id);
}

void Topo::set_local_rule()
{
    string body, dstip;
    vector<string> tasks;

    httplib::Client cli(ONOS_HOST);
    httplib::Headers headers = {{"Authorization", "Basic b25vczpyb2Nrcw=="}};
    cli.set_default_headers(headers);

    for (auto &node : nodes)
    {
        dstip = get_ip(node);
        tasks.emplace_back(format(rules::LOCAL_IP_BODY, node, dstip));
    }

    body = format(rules::BATCH_RULES, fmt::join(tasks, ","));
    auto res = cli.Post(ONOS_PATH, body, content_type);
    if (res)
    {
        spdlog::info("为每个节点设置到本地端口的流表！");
        spdlog::info("status: {}, body: {}", res->status, res->body);
    }
    else
    {
        spdlog::error("设置本地路由失败！Error: {}", to_string(res.error()));
    }
}

#endif
